import os
import subprocess
import tempfile
from dataclasses import dataclass, field
from typing import List, Optional
from contextlib import contextmanager
from .meta_flac import update_meta_flac
from .options import FFmpegBuilder, InputBuilder, OutputBuilder
from utils.temp import TempFileWriter

@dataclass
class AudioMeta:
    title: str
    album: str
    artists: List[str]

@dataclass
class UpdateMetadataParams:
    audio: str
    audio_ext: str
    meta: AudioMeta
    album_art: Optional[bytes] = None
    album_art_ext: Optional[str] = None

def extract_album_art(rd) -> bytes:
    cmd = [
        "ffmpeg",
        "-i", "pipe:0",  # input from stdin
        "-an",  # disable audio
        "-codec:v", "copy",  # copy video(image) codec
        "-f", "image2",  # use image2 muxer
        "pipe:1"  # output to stdout
    ]

    process = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
    stdout, stderr = process.communicate(input=rd.read())

    if process.returncode != 0:
        raise RuntimeError(f"ffmpeg run: {stderr.decode()}")

    return stdout

def update_meta(out_path: str, params: UpdateMetadataParams, logger) -> None:
    if params.audio_ext == ".flac":
        update_meta_flac(out_path, params, logger)
    else:
        update_meta_ffmpeg(out_path, params)

def update_meta_ffmpeg(out_path: str, params: UpdateMetadataParams) -> None:
    cmd = ["ffmpeg", "-y"]  # overwrite output file

    # input audio -> output audio
    cmd.extend(["-i", params.audio, "-map", "0:a", "-codec:a", "copy"])

    # input cover -> output cover
    if params.album_art and params.audio_ext != ".wav":
        with write_temp_file(params.album_art, params.album_art_ext) as art_path:
            cmd.extend(["-i", art_path, "-map", "1:v"])

            if params.audio_ext == ".ogg":
                cmd.extend(["-codec:v", "libtheora"])
            elif params.audio_ext == ".m4a":
                cmd.extend(["-codec:v", "mjpeg", "-disposition:v", "attached_pic"])
                cmd.extend(["-metadata:s:v", "title=Album cover", "-metadata:s:v", "comment=Cover (front)"])
            elif params.audio_ext == ".mp3":
                cmd.extend(["-codec:v", "mjpeg", "-metadata:s:v", "title=Album cover", "-metadata:s:v", "comment=Cover (front)"])

    # set file metadata
    if params.meta.album:
        cmd.extend(["-metadata", f"album={params.meta.album}"])

    if params.meta.title:
        cmd.extend(["-metadata", f"title={params.meta.title}"])

    if params.meta.artists:
        cmd.extend(["-metadata", f"artist={' / '.join(params.meta.artists)}"])

    if params.audio_ext == ".mp3":
        cmd.extend(["-write_id3v1", "true", "-id3v2_version", "3"])

    cmd.append(out_path)

    process = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
    stdout, stderr = process.communicate()

    if process.returncode != 0:
        raise RuntimeError(f"ffmpeg run: {stderr.decode()}")

    return None

@contextmanager
def write_temp_file(data, ext):
    with tempfile.NamedTemporaryFile(delete=False, suffix=ext) as tmp_file:
        tmp_file.write(data)
        tmp_file.flush()
        yield tmp_file.name
    os.remove(tmp_file.name)